package com.cloud.bssp.chinesenewyear.grabredenvelope;

import com.cloud.bssp.chinesenewyear.grabredenvelope.entity.PeopleDO;
import com.cloud.bssp.chinesenewyear.grabredenvelope.entity.RedEnvelopeDO;
import com.cloud.bssp.chinesenewyear.grabredenvelope.service.impl.GrabRedEnvelopeServiceImpl;
import com.cloud.bssp.chinesenewyear.grabredenvelope.service.impl.SendRedEnvelopeServiceImpl;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @description： 测试方法
 * @author：weirx
 * @date：2022/1/6 13:27
 * @version：3.0
 */
public class Test {

//    public static void main(String[] args) {
//        GlobalDataCache globalDataCache = GlobalInitProcessor.getGlobalDataCache();
//
//        // 准备用户
//        PeopleDO zhangsan = new PeopleDO(globalDataCache.getId(), "张三", new BigDecimal(100));
//        PeopleDO lisi = new PeopleDO(globalDataCache.getId(), "李四", new BigDecimal(100));
//        PeopleDO wangwu = new PeopleDO(globalDataCache.getId(), "王五", new BigDecimal(100));
//        Map<Integer, PeopleDO> map = new HashMap<>(4);
//        map.put(zhangsan.getId(), zhangsan);
//        map.put(lisi.getId(), lisi);
//        map.put(wangwu.getId(), wangwu);
//        globalDataCache.setPeopleCache(map);
//
//        //张三发100的红包
//        SendRedEnvelopeServiceImpl sendRedEnvelopeService = new SendRedEnvelopeServiceImpl();
//        RedEnvelopeDO redEnvelopeDO = new RedEnvelopeDO("新年快乐", new BigDecimal(100), 4, zhangsan.getId());
//        try {
//            sendRedEnvelopeService.send(redEnvelopeDO);
//        } catch (Exception e) {
//            System.out.println(e.getMessage());
//        }
//
//        //张三、李四、王五按【顺序】抢红包
//        LinkedList<Integer> userList = new LinkedList<>();
//        userList.add(zhangsan.getId());
//        userList.add(lisi.getId());
//        userList.add(wangwu.getId());
//
//        GrabRedEnvelopeServiceImpl grabRedEnvelopeService = new GrabRedEnvelopeServiceImpl();
//        userList.forEach(i -> {
//            try {
//                grabRedEnvelopeService.grab(i, redEnvelopeDO.getId());
//            } catch (Exception e) {
//                System.out.println(e.getMessage());
//            }
//        });
//
//        System.out.println(globalDataCache);
//
//        sum(globalDataCache);
//    }


    public static void main(String[] args) throws InterruptedException {
        // 定义一个常量，创建的用户数，也是抢红包的人数，同样是红包设定的个数（为了测试红包不足，实际会减少）
        int num = 10;

        //获取全局变量
        GlobalDataCache globalDataCache = GlobalInitProcessor.getGlobalDataCache();

        // 准备用户, Map初始化记得赋予初始大小，设置值 * 负载因子（0.75） = 实际容量
        Map<Integer, PeopleDO> peopleDOMap = new HashMap<>(128);
        for (int i = 0; i < num; i++) {
            //循环初始化用户，添加到peopleDOMap中
            PeopleDO peopleDO = new PeopleDO(globalDataCache.getId(), "用户：" + i, new BigDecimal(100));
            peopleDOMap.put(peopleDO.getId(), peopleDO);
        }
        // 设置用户到全局变量
        globalDataCache.setPeopleCache(peopleDOMap);

        //实例化发红包的业务实现
        SendRedEnvelopeServiceImpl sendRedEnvelopeService = new SendRedEnvelopeServiceImpl();
        //构造一个红包
        RedEnvelopeDO redEnvelopeDO = new RedEnvelopeDO(
                "新年快乐", new BigDecimal(100), num - 2, peopleDOMap.keySet().iterator().next());
        try {
            // 发送红包
            sendRedEnvelopeService.send(redEnvelopeDO);
        } catch (Exception e) {
            //此处会捕获手动抛出的“用户余额不足异常”
            System.out.println(e.getMessage());
        }

        // 获取钱红包实现
        GrabRedEnvelopeServiceImpl grabRedEnvelopeService = new GrabRedEnvelopeServiceImpl();

        // 使用屏障或者叫同步器，指定一个数字，当线程调用一个await方法，数字加1，知道等于设置的数值，所有线程才会开始执行，否则一直处于阻塞状态。
        CyclicBarrier cyclicBarrier = new CyclicBarrier(num);
        // 保证数据原子性，线程同步，等待线程处于阻塞状态，lock/unlock
        ReentrantLock lock = new ReentrantLock();
        peopleDOMap.forEach((k, v) -> {
            // 创建和人数一样多的线程
            new Thread(() -> {
                try {
                    // 等到所有线程到达
                    cyclicBarrier.await();
                    // 锁住抢红包方法grap，此时是互斥的，只有当前线程能进来，其余县城在阻塞队列等待
                    lock.lock();
                    // 执行抢红包方法
                    grabRedEnvelopeService.grab(k, redEnvelopeDO.getId());
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                } finally {
                    // 释放互斥锁，要保证在finally当中执行释放锁，防止死锁发生
                    lock.unlock();
                }
            }).start();
        });
        // 主线程休眠1秒，否则业务线程还没执行完，主线程就结束了，看不到结果
        TimeUnit.SECONDS.sleep(1);
        System.out.println(globalDataCache);

        // 结果统计
        sum(globalDataCache);
    }

//    public static void main(String[] args) throws InterruptedException {
//        // 定义一个常量，创建的用户数，也是抢红包的人数，同样是红包设定的个数（此场景就设置正好的数量吧）
//        int num = 10;
//
//        //获取全局变量
//        GlobalDataCache globalDataCache = GlobalInitProcessor.getGlobalDataCache();
//
//        // 准备用户, Map初始化记得赋予初始大小，设置值 * 负载因子（0.75） = 实际容量
//        Map<Integer, PeopleDO> peopleDOMap = new HashMap<>(128);
//        for (int i = 0; i < num; i++) {
//            //循环初始化用户，添加到peopleDOMap中
//            PeopleDO peopleDO = new PeopleDO(globalDataCache.getId(), "用户：" + i, new BigDecimal(100));
//            peopleDOMap.put(peopleDO.getId(), peopleDO);
//        }
//        // 设置用户到全局变量
//        globalDataCache.setPeopleCache(peopleDOMap);
//
//        //实例化发红包的业务实现
//        SendRedEnvelopeServiceImpl sendRedEnvelopeService = new SendRedEnvelopeServiceImpl();
//        //构造一个红包
//        RedEnvelopeDO redEnvelopeDO = new RedEnvelopeDO(
//                "新年快乐", new BigDecimal(100), num, peopleDOMap.keySet().iterator().next());
//        try {
//            // 发送红包
//            sendRedEnvelopeService.send(redEnvelopeDO);
//        } catch (Exception e) {
//            //此处会捕获手动抛出的“用户余额不足异常”
//            System.out.println(e.getMessage());
//        }
//
//        // 获取钱红包实现
//        GrabRedEnvelopeServiceImpl grabRedEnvelopeService = new GrabRedEnvelopeServiceImpl();
//
//        // 使用屏障或者叫同步器，指定一个数字，当线程调用一个await方法，数字加1，知道等于设置的数值，所有线程才会开始执行，否则一直处于阻塞状态。
//        CyclicBarrier cyclicBarrier = new CyclicBarrier(num);
//        // 并发的抢红包
//        peopleDOMap.forEach((k, v) -> {
//            // 创建和人数一样多的线程
//            new Thread(() -> {
//                try {
//                    // 等到所有线程到达
//                    cyclicBarrier.await();
//                    // 执行抢红包方法
//                    grabRedEnvelopeService.grab(k, redEnvelopeDO.getId());
//                } catch (Exception e) {
//                    System.out.println(e.getMessage());
//                }
//            }).start();
//        });
//        // 主线程休眠1秒，否则业务线程还没执行完，主线程就结束了，看不到结果
//        TimeUnit.SECONDS.sleep(1);
//        System.out.println(globalDataCache);
//
//        // 结果统计
//        sum(globalDataCache);
//    }

    /**
     * description: 计算总金额
     *
     * @param globalDataCache
     * @return: void
     * @author: weirx
     * @time: 2022/1/6 15:02
     */
    public static void sum(GlobalDataCache globalDataCache) {
        //计算被领取红包的总金额
        final BigDecimal[] count = {new BigDecimal(0)};
        globalDataCache.getEnvelopeLogCache().forEach((k, v) -> {
            v.forEach((k1, v1) -> {
                count[0] = count[0].add(v1.getAmount());
            });
        });
        System.out.println("被抢总金额：" + count[0]);

        //计算每个人的钱包总金额
        final BigDecimal[] count1 = {new BigDecimal(0)};
        globalDataCache.getPeopleCache().forEach((k, v) -> {
            count1[0] = count1[0].add(v.getAmount());
        });
        System.out.println("所有人的总金额：" + count1[0]);

        //红包剩余金额
        globalDataCache.getRedEnvelopeCache().forEach((k, v) -> {
            System.out.println("红包剩余金额：" + v.getAmount());
        });
    }
}
